package org.tool.core.lang.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;

/**
 * {@link Date} 相关工具类
 *
 * @author 李坤
 * @date 2022/6/17 13:00
 */
public abstract class DateUtil {

    public static final String MONTH_DAY = "MM-dd";
    public static final String YEAR_MONTH_DAY = "yyyy-MM-dd";
    public static final String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final HashMap<String, DateTimeFormatter> DATE_TIME_FORMATTERS = new HashMap<>();

    static {
        DATE_TIME_FORMATTERS.put(MONTH_DAY, DateTimeFormatter.ofPattern(MONTH_DAY));
        DATE_TIME_FORMATTERS.put(YEAR_MONTH_DAY, DateTimeFormatter.ofPattern(YEAR_MONTH_DAY));
    }

    /**
     * 格式化给定的日期
     *
     * @param date 时间日期
     * @return 结果
     */
    public static String format(Date date) {
        return format(date, YEAR_MONTH_DAY);
    }

    /**
     * 比较给定对象的日期是否相等(只比较日期，忽略时间)
     *
     * @return 比较结果
     */
    public static boolean equal4Date(Date date1, Date date2) {
        return DateUtil.date2LocalDate(date1).equals(DateUtil.date2LocalDate(date2));
    }

    /**
     * 根据给定的格式格式化日期
     *
     * @param date    时间日期
     * @param pattern 格式
     * @return 结果
     */
    public static String format(Date date, String pattern) {
        DateTimeFormatter formatter = getDateTimeFormatter(pattern);
        return date2LocalDateTime(date).format(formatter);
    }

    /**
     * String转为Date
     *
     * @param time 日期
     * @return Date
     */
    public static Date stringToDate(String time) throws ParseException {
        return stringToDate(time, YEAR_MONTH_DAY);
    }

    /**
     * String转为Date
     *
     * @param time    时间
     * @param pattern 字符串解析模板
     * @return Date
     */
    public static Date stringToDate(String time, String pattern) throws ParseException {
        return new SimpleDateFormat(pattern).parse(time);
    }

    /**
     * 根据给定的模板获取 {@link DateTimeFormatter} 对象实例
     *
     * @param pattern 给定的模板
     * @return 与模板对应的 {@link DateTimeFormatter} 对象实例
     */
    public static DateTimeFormatter getDateTimeFormatter(String pattern) {
        DateTimeFormatter formatter;

        if (Objects.isNull(formatter = DATE_TIME_FORMATTERS.get(pattern))) {

            synchronized (DateUtil.class) {

                if (Objects.isNull(formatter)) {
                    DATE_TIME_FORMATTERS.put(pattern, DateTimeFormatter.ofPattern(pattern));
                }
            }
        }
        return formatter;
    }

    /**
     * 对给定的日期进行操作
     *
     * @param date   给定的日期
     * @param offset 偏移量
     * @return 生成的新日期
     */
    public static Date offsetDay(Date date, int offset) {
        return offset(date, Calendar.DAY_OF_MONTH, offset);
    }

    /**
     * 对给定的日期进行操作
     *
     * @param date   给定的日期
     * @param offset 偏移量
     * @param field  偏移单位
     * @return 生成的新日期
     */
    public static Date offset(Date date, int offset, int field) {
        final Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(field, offset);

        return calendar.getTime();
    }

    /**
     * localDateTime转为Date
     *
     * @param localDateTime 日期
     * @return Date 日期
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime atZone = localDateTime.atZone(zoneId);

        return Date.from(atZone.toInstant());
    }

    /**
     * Date转LocalDate
     *
     * @param date 日期
     */
    public static LocalDate date2LocalDate(Date date) {
        Instant instant = date.toInstant();
        ZonedDateTime atZone = instant.atZone(ZoneId.systemDefault());

        return atZone.toLocalDate();
    }

    /**
     * Date转LocalDateTime
     *
     * @param date 日期
     */
    public static LocalDateTime date2LocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZonedDateTime atZone = instant.atZone(ZoneId.systemDefault());

        return atZone.toLocalDateTime();
    }

    /**
     * 获取给定年份的第一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param year 给定的年份
     * @return 正确的日期
     */
    public static Date getFirstDateOfYear2Date(int year) {
        return getFirstDateOfYear(year).getTime();
    }

    /**
     * 获取给定年份的最后一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param year 给定的年份
     * @return 正确的日期
     */
    public static Date getLastDateOfYear2Date(int year) {
        final Calendar calendar = getFirstDateOfYear(year + 1);
        calendar.add(Calendar.DAY_OF_YEAR, -1);

        return calendar.getTime();
    }

    /**
     * 获取给定年份的第一天对应的日期
     *
     * @param year 给定的年份
     * @return 正确的日期
     */
    public static Calendar getFirstDateOfYear(int year) {
        final Calendar calendar = Calendar.getInstance();

        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_YEAR, 1);

        return calendar;
    }

    /**
     * 获取给定日期对应的季度
     *
     * @param date 给定的日期
     * @return 对应的季度
     */
    public static int getQuarter4Month(Date date) {
        return (date.getMonth() / 3) + 1;
    }

    /**
     * 获取给定月份对应的季度
     *
     * @param month 给定的月份
     * @return 对应的季度
     */
    public static int getQuarter4Month(int month) {
        return ((month - 1) / 3) + 1;
    }

    /**
     * 获取给定日期的第一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param date 给定的日期
     * @return 正确的日期
     */
    public static Date getFirstDateOfMonth2Date(Date date) {
        final Calendar calendar = convert(date);
        return getFirstDateOfMonth2Date(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
    }

    /**
     * 获取给定月份的第一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param year  给定的年份
     * @param month 给定的月份
     * @return 正确的日期
     */
    public static Date getFirstDateOfMonth2Date(int year, int month) {
        return getFirstDateOfMonth(year, month).getTime();
    }

    /**
     * 获取给定日期的最后一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param date 给定的日期
     * @return 正确的日期
     */
    public static Date getLastDateOfMonth2Date(Date date) {
        final Calendar calendar = convert(date);
        return getLastDateOfMonth2Date(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
    }

    /**
     * 获取给定月份的最后一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param year  给定的年份
     * @param month 给定的月份
     * @return 正确的日期
     */
    public static Date getLastDateOfMonth2Date(int year, int month) {
        // 日期可能是直接从 Date 中获取的
        if (year < 1000) {
            year += 1900;
        }
        final Calendar calendar = getFirstDateOfMonth(year, month + 1);
        calendar.add(Calendar.DAY_OF_MONTH, -1);

        return calendar.getTime();
    }

    /**
     * 获取给定日期的季度第一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param date 给定的日期
     * @return 正确的日期
     */
    public static Date getFirstDateOfQuarter2Date(Date date) {
        final Calendar calendar = convert(date);
        return getFirstDateOfQuarter2Date(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
    }

    /**
     * 获取给定月份的对应季度的第一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param year  给定的年份
     * @param month 给定的月份
     * @return 正确的日期
     */
    public static Date getFirstDateOfQuarter2Date(int year, int month) {
        month = (getQuarter4Month(month) - 1) * 3;
        return getFirstDateOfMonth2Date(year, month + 1);
    }

    /**
     * 获取给定日期的季度最后一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param date 给定的日期
     * @return 正确的日期
     */
    public static Date getLastDateOfQuarter2Date(Date date) {
        final Calendar calendar = convert(date);
        return getLastDateOfMonth2Date(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
    }

    /**
     * 获取给定月份的对应季度的最后一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param year  给定的年份
     * @param month 给定的月份
     * @return 正确的日期
     */
    public static Date getLastDateOfQuarter2Date(int year, int month) {
        month = getQuarter4Month(month) * 3;
        return getLastDateOfMonth2Date(year, month);
    }

    /**
     * 获取给定日期的第一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param date 给定的日期
     * @return 正确的日期
     */
    public static Date getFirstDateOfYear2Date(Date date) {
        final Calendar calendar = convert(date);
        return getFirstDateOfYear2Date(calendar.get(Calendar.YEAR));
    }

    /**
     * 获取给定日期的最后一天对应的日期，并将其转换为 {@link Date} 类型
     *
     * @param date 给定的日期
     * @return 正确的日期
     */
    public static Date getLastDateOfYear2Date(Date date) {
        final Calendar calendar = convert(date);
        return getLastDateOfYear2Date(calendar.get(Calendar.YEAR));
    }


    /**
     * 获取给定年份、月份的第一天对应的日期
     *
     * @param year  给定的年份
     * @param month 给定月份
     * @return 正确的日期
     */
    public static Calendar getFirstDateOfMonth(int year, int month) {
        final Calendar calendar = Calendar.getInstance();

        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        return calendar;
    }

    /**
     * 将 {@link Date} 对象转换为 {@link Calendar} 对象
     *
     * @param target 待转换的Date对象实例
     * @return 转换后的对象实例
     */
    public static Calendar convert(Date target) {
        final Calendar instance = Calendar.getInstance();
        instance.setTime(target);

        return instance;
    }

}
